//Author: Soeren Pirk
//Date: 11.04.2013
//Edited: Thomas Lindemeier
//Date: 31.03.2014

#include "src/Common.h"
#include "src/Renderer.h"
#include "src/Scene.h"
#include <iostream>

// global functions
void init();
void shutdown();

// event handler declarations
void glfw_onKey(GLFWwindow * window, GLint key, GLint scancode, GLint action, GLint mods);
void glfw_onMouseMove(GLFWwindow * window, GLdouble x, GLdouble y);
void glfw_onMouse(GLFWwindow * window, GLint button, GLint action, GLint mods);
void glfw_onScroll(GLFWwindow * window, GLdouble xo, GLdouble yo);
void glfw_onResize(GLFWwindow * window, GLint width, GLint height);

GLFWwindow *		g_glfwWindow		= NULL;
Renderer *			g_renderer			= NULL;
Scene    *			g_scene				= NULL;
vec2				g_oldMouse			= vec2(0, 0);
GLboolean			g_leftButton		= GL_FALSE;
GLboolean			g_rightButton		= GL_FALSE;
const GLint			g_width				= 1024;
const GLint			g_height			= 768;
const std::string	g_projectName		= "framework";


// error callback - GLFW calls this on errors 
void onError(GLint errorCode, const GLchar* errorMessage)
{
	std::cerr << "GLWindow::ERROR:\t" << errorMessage << std::endl;
	std::cerr << "press continue to exit";
	std::cin.get();
	exit(EXIT_FAILURE);
}

// initialize GLFW, glew and OpenGL
// creates a window
void init()
{    
    if(!glfwInit())
        exit( EXIT_FAILURE );

	// initialize GLFW
	GLboolean error = glfwInit();
	if (!error)
	{
		std::cerr << "could not init GLFW:" << std::endl;
		std::cerr << "press enter to exit";
		std::cin.get();
		exit(EXIT_FAILURE);
	}
	glfwSetErrorCallback(onError);

	// set depths
	glfwWindowHint(GLFW_RED_BITS, 8);
	glfwWindowHint(GLFW_GREEN_BITS, 8);
	glfwWindowHint(GLFW_BLUE_BITS, 8);
	glfwWindowHint(GLFW_ALPHA_BITS, 8);
	glfwWindowHint(GLFW_STENCIL_BITS, 16);
	glfwWindowHint(GLFW_DEPTH_BITS, 16);
	glfwWindowHint(GLFW_SAMPLES, 4);

	glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // allow the window to be resized

	// create window
	g_glfwWindow = glfwCreateWindow(g_width, g_height, g_projectName.c_str(), NULL, NULL);

	// set callbacks
	glfwSetKeyCallback(g_glfwWindow, glfw_onKey);
	glfwSetWindowSizeCallback(g_glfwWindow, glfw_onResize);
	glfwSetMouseButtonCallback(g_glfwWindow, glfw_onMouse);
	glfwSetCursorPosCallback(g_glfwWindow, glfw_onMouseMove);
	glfwSetScrollCallback(g_glfwWindow, glfw_onScroll);

	// set current gl context
	glfwMakeContextCurrent(g_glfwWindow);

	// initialize GLEW
	GLenum err = glewInit();
	if (GLEW_OK != err)
	{
		std::cerr << "Error: " << glewGetErrorString(err);
		std::cerr << "press enter to exit";
		std::cin.get();
		exit(EXIT_FAILURE);
	}

	std::cout << "using OpenGL version: " << glGetString(GL_VERSION) << std::endl;

    g_scene = new Scene();
    g_renderer = new Renderer(g_scene);
	g_renderer->resize(g_width, g_height);
    g_renderer->init();	
}

void glfw_onKey(GLFWwindow * window, GLint key, GLint scancode, GLint action, GLint mods)
{
    if(action == GLFW_PRESS)
    {
        if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
		{
            shutdown();
		}
		
        if (key == 'M' && action == GLFW_PRESS)
        {
            g_scene->m_showMesh = !g_scene->m_showMesh;
        }
		if (key == 'Y' && action == GLFW_PRESS)
		{
			g_scene->culling = !g_scene->culling;
		}
        if (key == 294 && action == GLFW_PRESS) // 294 entspricht F5
        {
			g_scene->reloadShader();
        }
/*
		if (glfwGetKey(window, 'H') && (glfwGetKey(window, 61) || glfwGetKey(window, 334)) && action == GLFW_PRESS)
        {
            g_scene->setHSVValues(0.01f, 0.0f, 0.0f);
        }

		if (glfwGetKey(window, 'H') && (glfwGetKey(window, 45) || glfwGetKey(window, 333)) && action == GLFW_PRESS)
        {
            g_scene->setHSVValues(-0.01f, 0.0f, 0.0f);
        }

		if (glfwGetKey(window, 'S') && (glfwGetKey(window, 61) || glfwGetKey(window, 334)) && action == GLFW_PRESS)
        {
            g_scene->setHSVValues(0.0f, 0.05f, 0.0f);
        }

		if (glfwGetKey(window, 'S') && (glfwGetKey(window, 45) || glfwGetKey(window, 333)) && action == GLFW_PRESS)
        {
            g_scene->setHSVValues(0.0f, -0.05f, 0.0f);
        }

		if (glfwGetKey(window, 'V') && (glfwGetKey(window, 61) || glfwGetKey(window, 334)) && action == GLFW_PRESS)
        {
            g_scene->setHSVValues(0.0f, 0.0f, 0.05f);
        }

		if (glfwGetKey(window, 'V') && (glfwGetKey(window, 45) || glfwGetKey(window, 333)) && action == GLFW_PRESS)
        {
            g_scene->setHSVValues(0.0f, 0.0f, -0.05f);
        }

        if (key == 'C' && action == GLFW_PRESS)
        {
            g_scene->toggleCulling();
        }		
        if (key == 'F' && action == GLFW_PRESS)
        {
            g_scene->toggleFisheye();
        }
        if (key == 'P' && action == GLFW_PRESS)
        {
            g_scene->toggleProjection();
        }
        if (key == 'R' && action == GLFW_PRESS)
        {
            g_scene->toggleRotation();
        }
*/
    }
}

void glfw_onMouseMove(GLFWwindow * window, GLdouble x, GLdouble y)
{
    vec2 mouse((GLfloat)x, (GLfloat)y);
    vec2 d = mouse - g_oldMouse;

    if(g_leftButton)
        g_renderer->onMouseMove(d);

    g_oldMouse = mouse;
}

void glfw_onMouse(GLFWwindow * window, GLint button, GLint action, GLint mods)
{
    if(button == GLFW_MOUSE_BUTTON_LEFT)
    {
        if(action == GLFW_PRESS)
            g_leftButton = GL_TRUE;
        else
            g_leftButton = GL_FALSE;
    }

    if(button == GLFW_MOUSE_BUTTON_RIGHT)
    {
        if(action == GLFW_PRESS)
            g_rightButton = GL_TRUE;
        else
            g_rightButton = GL_FALSE;
    }
}

void glfw_onScroll(GLFWwindow * window, GLdouble xo, GLdouble yo)
{
    g_renderer->onMouseWheel(yo < 0);
}

void glfw_onResize(GLFWwindow * window, GLint width, GLint height)
{
    if(g_renderer)
        g_renderer->resize(width, height);
}

void shutdown()
{
    delete g_renderer;
    delete g_scene;

    exit(EXIT_SUCCESS);
}

GLint main( void )
{
    init();

	while (!glfwWindowShouldClose(g_glfwWindow))
    {
        g_renderer->render();

		glfwPollEvents();
		glfwSwapBuffers(g_glfwWindow);
	}

	shutdown();
	glfwTerminate();
}
